import _ from 'lodash'
import App from '../../base'
import './services/project-list'
App.controller('ProjectPageController', function(
  $scope,
  $modal,
  $window,
  queuedHttp,
  eventTracking, // eslint-disable-line camelcase
  $timeout,
  localStorage,
  ProjectListService
) {
  $scope.projects = window.data.projects
  $scope.tags = window.data.tags
  $scope.notifications = window.data.notifications
  $scope.notificationsInstitution = window.data.notificationsInstitution
  $scope.allSelected = false
  $scope.selectedProjects = []
  $scope.filter = 'all'
  $scope.predicate = 'lastUpdated'
  $scope.nUntagged = 0
  $scope.reverse = true
  $scope.searchText = { value: '' }
  $scope.$watch('predicate', function(newValue) {
    $scope.comparator =
      newValue === 'ownerName' ? ownerNameComparator : defaultComparator
  })

  $timeout(() => recalculateProjectListHeight(), 10)

  $scope.$watch(
    () =>
      $scope.projects.filter(
        project =>
          (project.tags == null || project.tags.length === 0) &&
          !project.archived &&
          !project.trashed
      ).length,
    newVal => ($scope.nUntagged = newVal)
  )

  var recalculateProjectListHeight = function() {
    const $projListCard = $('.project-list-card')
    if (!$projListCard || !$projListCard.offset()) return

    const topOffset = $projListCard.offset().top
    const cardPadding = $projListCard.outerHeight() - $projListCard.height()
    const bottomOffset = $('footer').outerHeight()
    const height = $window.innerHeight - topOffset - bottomOffset - cardPadding
    $scope.projectListHeight = height
  }

  function defaultComparator(v1, v2) {
    var result = 0
    var type1 = v1.type
    var type2 = v2.type

    if ($scope.predicate === 'ownerName') {
      return
    }

    if (type1 === type2) {
      var value1 = v1.value
      var value2 = v2.value

      if (type1 === 'string') {
        // Compare strings case-insensitively
        value1 = value1.toLowerCase()
        value2 = value2.toLowerCase()
      } else if (type1 === 'object') {
        // For basic objects, use the position of the object
        // in the collection instead of the value
        if (angular.isObject(value1)) value1 = v1.index
        if (angular.isObject(value2)) value2 = v2.index
      }

      if (value1 !== value2) {
        result = value1 < value2 ? -1 : 1
      }
    } else {
      result = type1 < type2 ? -1 : 1
    }

    return result
  }

  function ownerNameComparator(v1, v2) {
    if ($scope.predicate !== 'ownerName') {
      return
    }
    if (v1.value === 'You') {
      if (v2.value === 'You') {
        return v1.index < v2.index ? -1 : 1
      } else {
        return 1
      }
    } else if (v1.value === 'An Overleaf v1 User' || v1.value === 'None') {
      if (v2.value === 'An Overleaf v1 User' || v2.value === 'None') {
        return v1.index < v2.index ? -1 : 1
      } else {
        return -1
      }
    } else {
      if (v2.value === 'You') {
        return -1
      } else if (v2.value === 'An Overleaf v1 User' || v2.value === 'None') {
        return 1
      } else {
        return v1.value > v2.value ? -1 : 1
      }
    }
  }

  angular.element($window).bind('resize', function() {
    recalculateProjectListHeight()
    $scope.$apply()
  })

  $scope.$on('project-list:notifications-received', () =>
    $scope.$applyAsync(() => recalculateProjectListHeight())
  )

  // Allow tags to be accessed on projects as well
  const projectsById = {}
  for (let project of $scope.projects) {
    projectsById[project.id] = project
  }

  $scope.getProjectById = id => projectsById[id]

  for (let tag of $scope.tags) {
    for (let projectId of tag.project_ids || []) {
      let project = projectsById[projectId]
      if (project) {
        if (!project.tags) {
          project.tags = []
        }
        project.tags.push(tag)
      }
    }
  }

  $scope.changePredicate = function(newPredicate) {
    if ($scope.predicate === newPredicate) {
      $scope.reverse = !$scope.reverse
    }
    $scope.predicate = newPredicate
  }

  $scope.getSortIconClass = function(column) {
    if (column === $scope.predicate && $scope.reverse) {
      return 'fa-caret-down'
    } else if (column === $scope.predicate && !$scope.reverse) {
      return 'fa-caret-up'
    } else {
      return ''
    }
  }

  $scope.searchProjects = function() {
    eventTracking.send(
      'project-list-page-interaction',
      'project-search',
      'keydown'
    )
    $scope.updateVisibleProjects()
  }

  $scope.clearSearchText = function() {
    $scope.searchText.value = ''
    $scope.filter = 'all'
    $scope.$emit('search:clear')
    $scope.updateVisibleProjects()
  }

  $scope.setFilter = function(filter) {
    $scope.filter = filter
    $scope.updateVisibleProjects()
  }

  $scope.updateSelectedProjects = function() {
    $scope.selectedProjects = $scope.projects.filter(
      project => project.selected
    )
  }

  $scope.getSelectedProjects = () => $scope.selectedProjects

  $scope.getSelectedProjectIds = () =>
    $scope.selectedProjects.map(project => project.id)

  $scope.getFirstSelectedProject = () => $scope.selectedProjects[0]

  $scope.hasLeavableProjectsSelected = () =>
    _.some(
      $scope.getSelectedProjects(),
      project => project.accessLevel !== 'owner' && project.trashed
    )

  $scope.hasDeletableProjectsSelected = () =>
    _.some(
      $scope.getSelectedProjects(),
      project => project.accessLevel === 'owner' && project.trashed
    )

  $scope.updateVisibleProjects = function() {
    $scope.visibleProjects = []
    const selectedTag = $scope.getSelectedTag()
    for (let project of $scope.projects) {
      let visible = true
      // Only show if it matches any search text
      if ($scope.searchText.value !== '') {
        if (
          project.name
            .toLowerCase()
            .indexOf($scope.searchText.value.toLowerCase()) === -1
        ) {
          visible = false
        }
      }
      // Only show if it matches the selected tag
      if (
        $scope.filter === 'tag' &&
        selectedTag != null &&
        !selectedTag.project_ids.includes(project.id)
      ) {
        visible = false
      }

      // Hide tagged projects if we only want to see the uncategorized ones
      if (
        $scope.filter === 'untagged' &&
        (project.tags != null ? project.tags.length : undefined) > 0
      ) {
        visible = false
      }

      // Hide projects we own if we only want to see shared projects
      if ($scope.filter === 'shared' && project.accessLevel === 'owner') {
        visible = false
      }

      // Hide projects we don't own if we only want to see owned projects
      if ($scope.filter === 'owned' && project.accessLevel !== 'owner') {
        visible = false
      }

      if ($scope.filter === 'archived') {
        // Only show archived projects
        if (!project.archived) {
          visible = false
        }
      } else {
        // Only show non-archived projects
        if (project.archived) {
          visible = false
        }
      }

      if ($scope.filter === 'trashed') {
        // Only show trashed projects
        if (!project.trashed) {
          visible = false
        }
      } else {
        // Only show non-trashed projects
        if (project.trashed) {
          visible = false
        }
      }

      if (visible) {
        $scope.visibleProjects.push(project)
      } else {
        // We don't want hidden selections
        project.selected = false
      }
    }

    localStorage(
      'project_list',
      JSON.stringify({
        filter: $scope.filter,
        selectedTagId: selectedTag != null ? selectedTag._id : undefined
      })
    )
    $scope.updateSelectedProjects()
  }

  $scope.getSelectedTag = function() {
    for (let tag of $scope.tags) {
      if (tag.selected) {
        return tag
      }
    }
    return null
  }

  $scope._removeProjectIdsFromTagArray = function(tag, removeProjectIds) {
    // Remove project_id from tag.project_ids
    const remainingProjectIds = []
    const removedProjectIds = []
    for (let projectId of tag.project_ids) {
      if (!removeProjectIds.includes(projectId)) {
        remainingProjectIds.push(projectId)
      } else {
        removedProjectIds.push(projectId)
      }
    }
    tag.project_ids = remainingProjectIds
    return removedProjectIds
  }

  $scope._removeProjectFromList = function(project) {
    const index = $scope.projects.indexOf(project)
    if (index > -1) {
      $scope.projects.splice(index, 1)
    }
  }

  $scope.removeSelectedProjectsFromTag = function(tag) {
    tag.showWhenEmpty = true

    const selectedProjectIds = $scope.getSelectedProjectIds()
    const selectedProjects = $scope.getSelectedProjects()

    const removedProjectIds = $scope._removeProjectIdsFromTagArray(
      tag,
      selectedProjectIds
    )

    // Remove tag from project.tags
    for (let project of selectedProjects) {
      if (!project.tags) {
        project.tags = []
      }
      const index = project.tags.indexOf(tag)
      if (index > -1) {
        project.tags.splice(index, 1)
      }
    }

    for (let projectId of removedProjectIds) {
      queuedHttp({
        method: 'DELETE',
        url: `/tag/${tag._id}/project/${projectId}`,
        headers: {
          'X-CSRF-Token': window.csrfToken
        }
      })
    }

    // If we're filtering by this tag then we need to remove
    // the projects from view
    $scope.updateVisibleProjects()
  }

  $scope.removeProjectFromTag = function(project, tag) {
    tag.showWhenEmpty = true

    if (!project.tags) {
      project.tags = []
    }
    const index = project.tags.indexOf(tag)

    if (index > -1) {
      $scope._removeProjectIdsFromTagArray(tag, [project.id])
      project.tags.splice(index, 1)
      queuedHttp({
        method: 'DELETE',
        url: `/tag/${tag._id}/project/${project.id}`,
        headers: {
          'X-CSRF-Token': window.csrfToken
        }
      })
      $scope.updateVisibleProjects()
    }
  }

  $scope.addSelectedProjectsToTag = function(tag) {
    const selectedProjects = $scope.getSelectedProjects()
    eventTracking.send(
      'project-list-page-interaction',
      'project action',
      'addSelectedProjectsToTag'
    )

    // Add project_ids into tag.project_ids
    const addedProjectIds = []
    for (let projectId of $scope.getSelectedProjectIds()) {
      if (!tag.project_ids.includes(projectId)) {
        tag.project_ids.push(projectId)
        addedProjectIds.push(projectId)
      }
    }

    // Add tag into each project.tags
    for (let project of selectedProjects) {
      if (!project.tags) {
        project.tags = []
      }
      if (!project.tags.includes(tag)) {
        project.tags.push(tag)
      }
    }

    for (let projectId of addedProjectIds) {
      queuedHttp.post(`/tag/${tag._id}/project/${projectId}`, {
        _csrf: window.csrfToken
      })
    }
  }

  $scope.openNewTagModal = function(e) {
    const modalInstance = $modal.open({
      templateUrl: 'newTagModalTemplate',
      controller: 'NewTagModalController'
    })

    modalInstance.result.then(function(tag) {
      const tagIsDuplicate = $scope.tags.find(function(existingTag) {
        return tag.name === existingTag.name
      })

      if (!tagIsDuplicate) {
        $scope.tags.push(tag)
        $scope.addSelectedProjectsToTag(tag)
      }
    })
  }

  $scope.createProject = function(name, template) {
    if (template == null) {
      template = 'none'
    }
    return queuedHttp
      .post('/project/new', {
        _csrf: window.csrfToken,
        projectName: name,
        template
      })
      .then(function(response) {
        const { data } = response
        $scope.projects.push({
          name,
          _id: data.project_id,
          accessLevel: 'owner',
          owner: data.owner
          // TODO: Check access level if correct after adding it in
          // to the rest of the app
        })
        $scope.updateVisibleProjects()
      })
  }

  $scope.openCreateProjectModal = function(template) {
    if (template == null) {
      template = 'none'
    }
    eventTracking.send('project-list-page-interaction', 'new-project', template)
    const modalInstance = $modal.open({
      templateUrl: 'newProjectModalTemplate',
      controller: 'NewProjectModalController',
      resolve: {
        template() {
          return template
        }
      },
      scope: $scope
    })

    modalInstance.result.then(
      projectId => (window.location = `/project/${projectId}`)
    )
  }

  $scope.renameProject = (project, newName) =>
    queuedHttp
      .post(`/project/${project.id}/rename`, {
        newProjectName: newName,
        _csrf: window.csrfToken
      })
      .then(() => (project.name = newName))

  $scope.openRenameProjectModal = function() {
    let project = $scope.getFirstSelectedProject()
    if (!project || project.accessLevel !== 'owner') {
      return
    }
    eventTracking.send(
      'project-list-page-interaction',
      'project action',
      'Rename'
    )
    $modal.open({
      templateUrl: 'renameProjectModalTemplate',
      controller: 'RenameProjectModalController',
      resolve: {
        project() {
          return project
        }
      },
      scope: $scope
    })
  }

  $scope.cloneProject = function(project, cloneName) {
    eventTracking.send(
      'project-list-page-interaction',
      'project action',
      'Clone'
    )
    return queuedHttp
      .post(`/project/${project.id}/clone`, {
        _csrf: window.csrfToken,
        projectName: cloneName
      })
      .then(function(response) {
        const { data } = response
        $scope.projects.push({
          name: data.name,
          id: data.project_id,
          accessLevel: 'owner',
          owner: data.owner
          // TODO: Check access level if correct after adding it in
          // to the rest of the app
        })
        $scope.updateVisibleProjects()
      })
  }

  $scope.openCloneProjectModal = function() {
    let project = $scope.getFirstSelectedProject()
    if (!project) {
      return
    }

    $modal.open({
      templateUrl: 'cloneProjectModalTemplate',
      controller: 'CloneProjectModalController',
      resolve: {
        project() {
          return project
        }
      },
      scope: $scope
    })
  }

  // Methods to create modals for archiving, trashing, leaving and deleting projects
  const _createArchiveTrashLeaveOrDeleteProjectsModal = function(
    action,
    projects
  ) {
    eventTracking.send(
      'project-list-page-interaction',
      'project action',
      action
    )
    return $modal.open({
      templateUrl: 'archiveTrashLeaveOrDeleteProjectsModalTemplate',
      controller: 'ArchiveTrashLeaveOrDeleteProjectsModalController',
      resolve: {
        projects() {
          return projects
        },
        action() {
          return action
        }
      }
    })
  }

  $scope.createArchiveProjectsModal = function(projects) {
    return _createArchiveTrashLeaveOrDeleteProjectsModal('archive', projects)
  }

  $scope.createTrashProjectsModal = function(projects) {
    return _createArchiveTrashLeaveOrDeleteProjectsModal('trash', projects)
  }

  $scope.createLeaveProjectsModal = function(projects) {
    return _createArchiveTrashLeaveOrDeleteProjectsModal('leave', projects)
  }

  $scope.createDeleteProjectsModal = function(projects) {
    return _createArchiveTrashLeaveOrDeleteProjectsModal('delete', projects)
  }

  $scope.createLeaveOrDeleteProjectsModal = function(projects) {
    return _createArchiveTrashLeaveOrDeleteProjectsModal(
      'leaveOrDelete',
      projects
    )
  }

  //
  $scope.openArchiveProjectsModal = function() {
    const modalInstance = $scope.createArchiveProjectsModal(
      $scope.getSelectedProjects()
    )
    modalInstance.result.then(() => $scope.archiveSelectedProjects())
  }

  $scope.openTrashProjectsModal = function() {
    const modalInstance = $scope.createTrashProjectsModal(
      $scope.getSelectedProjects()
    )

    modalInstance.result.then(() => $scope.trashSelectedProjects())
  }

  $scope.openLeaveProjectsModal = function() {
    const modalInstance = $scope.createLeaveProjectsModal(
      $scope.getSelectedProjects()
    )
    modalInstance.result.then(() => $scope.leaveSelectedProjects())
  }

  $scope.openDeleteProjectsModal = function() {
    const modalInstance = $scope.createDeleteProjectsModal(
      $scope.getSelectedProjects()
    )
    modalInstance.result.then(() => $scope.deleteSelectedProjects())
  }

  $scope.openLeaveOrDeleteProjectsModal = function() {
    const modalInstance = $scope.createLeaveOrDeleteProjectsModal(
      $scope.getSelectedProjects()
    )
    modalInstance.result.then(() => $scope.leaveOrDeleteSelectedProjects())
  }

  //
  $scope.archiveSelectedProjects = () =>
    $scope.archiveProjects($scope.getSelectedProjects())

  $scope.unarchiveSelectedProjects = () =>
    $scope.unarchiveProjects($scope.getSelectedProjects())

  $scope.trashSelectedProjects = () =>
    $scope.trashProjects($scope.getSelectedProjects())

  $scope.untrashSelectedProjects = () =>
    $scope.untrashProjects($scope.getSelectedProjects())

  $scope.leaveSelectedProjects = () =>
    $scope.leaveProjects($scope.getSelectedProjects())

  $scope.deleteSelectedProjects = () =>
    $scope.deleteProjects($scope.getSelectedProjects())

  $scope.leaveOrDeleteSelectedProjects = () =>
    $scope.leaveOrDeleteProjects($scope.getSelectedProjects())

  //
  $scope.archiveProjects = function(projects) {
    for (let project of projects) {
      project.archived = true
      project.trashed = false
      _archiveProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.unarchiveProjects = function(projects) {
    for (let project of projects) {
      project.archived = false
      _unarchiveProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.trashProjects = function(projects) {
    for (let project of projects) {
      project.trashed = true
      project.archived = false
      _trashProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.untrashProjects = function(projects) {
    for (let project of projects) {
      project.trashed = false
      _untrashProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.leaveProjects = function(projects) {
    _deleteOrLeaveProjectsLocally(projects)
    for (let project of projects) {
      _leaveProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.deleteProjects = function(projects) {
    _deleteOrLeaveProjectsLocally(projects)
    for (let project of projects) {
      _deleteProject(project)
    }
    $scope.updateVisibleProjects()
  }

  $scope.leaveOrDeleteProjects = function(projects) {
    _deleteOrLeaveProjectsLocally(projects)
    for (let project of projects) {
      if (project.accessLevel === 'owner') {
        _deleteProject(project)
      } else {
        _leaveProject(project)
      }
    }
    $scope.updateVisibleProjects()
  }

  // Actual interaction with the backend---we could move this into a service
  const _archiveProject = function(project) {
    return queuedHttp({
      method: 'POST',
      url: `/project/${project.id}/archive`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _unarchiveProject = function(project) {
    return queuedHttp({
      method: 'DELETE',
      url: `/project/${project.id}/archive`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _trashProject = function(project) {
    return queuedHttp({
      method: 'POST',
      url: `/project/${project.id}/trash`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _untrashProject = function(project) {
    return queuedHttp({
      method: 'DELETE',
      url: `/project/${project.id}/trash`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _leaveProject = function(project) {
    return queuedHttp({
      method: 'POST',
      url: `/project/${project.id}/leave`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _deleteProject = function(project) {
    return queuedHttp({
      method: 'DELETE',
      url: `/project/${project.id}`,
      headers: {
        'X-CSRF-Token': window.csrfToken
      }
    })
  }

  const _deleteOrLeaveProjectsLocally = function(projects) {
    const projectIds = projects.map(p => p.id)
    for (let tag of $scope.tags || []) {
      $scope._removeProjectIdsFromTagArray(tag, projectIds)
    }
    for (let project of projects || []) {
      $scope._removeProjectFromList(project)
    }
  }

  $scope.getValueForCurrentPredicate = function(project) {
    if ($scope.predicate === 'ownerName') {
      return ProjectListService.getOwnerName(project)
    } else {
      return project[$scope.predicate]
    }
  }

  $scope.openUploadProjectModal = function() {
    $modal.open({
      templateUrl: 'uploadProjectModalTemplate',
      controller: 'UploadProjectModalController'
    })
  }

  $scope.downloadSelectedProjects = () =>
    $scope.downloadProjectsById($scope.getSelectedProjectIds())

  $scope.downloadProjectsById = function(projectIds) {
    let path
    eventTracking.send(
      'project-list-page-interaction',
      'project action',
      'Download Zip'
    )
    if (projectIds.length > 1) {
      path = `/project/download/zip?project_ids=${projectIds.join(',')}`
    } else {
      path = `/project/${projectIds[0]}/download/zip`
    }
    return (window.location = path)
  }

  const markTagAsSelected = id => {
    for (let tag of $scope.tags) {
      if (tag._id === id) {
        tag.selected = true
      } else {
        tag.selected = false
      }
    }
  }

  const storedUIOpts = JSON.parse(localStorage('project_list'))

  if (storedUIOpts && storedUIOpts.filter) {
    if (storedUIOpts.filter === 'tag' && storedUIOpts.selectedTagId) {
      markTagAsSelected(storedUIOpts.selectedTagId)
    }
    $scope.setFilter(storedUIOpts.filter)
  } else {
    $scope.updateVisibleProjects()
  }
})

App.controller('ProjectListItemController', function(
  $scope,
  $modal,
  queuedHttp,
  ProjectListService
) {
  $scope.projectLink = function(project) {
    return `/project/${project.id}`
  }

  $scope.isLinkSharingProject = project => project.source === 'token'

  $scope.hasGenericOwnerName = () => {
    /* eslint-disable camelcase */
    const { first_name, last_name, email } = $scope.project.owner
    return !first_name && !last_name && !email
    /* eslint-enable camelcase */
  }

  $scope.getOwnerName = ProjectListService.getOwnerName

  $scope.getUserName = ProjectListService.getUserName

  $scope.isOwner = () =>
    $scope.project.owner && window.user_id === $scope.project.owner._id

  $scope.$watch('project.selected', function(value) {
    if (value != null) {
      $scope.updateSelectedProjects()
    }
  })

  $scope.clone = function(e) {
    e.stopPropagation()
    $scope.project.isTableActionInflight = true
    return $scope
      .cloneProject($scope.project, `${$scope.project.name} (Copy)`)
      .then(() => ($scope.project.isTableActionInflight = false))
      .catch(function(response) {
        const { data, status } = response
        const error = status === 400 ? { message: data } : true
        $modal.open({
          templateUrl: 'showErrorModalTemplate',
          controller: 'ShowErrorModalController',
          resolve: {
            error() {
              return error
            }
          }
        })
        $scope.project.isTableActionInflight = false
      })
  }

  $scope.download = function(e) {
    e.stopPropagation()
    $scope.downloadProjectsById([$scope.project.id])
  }

  $scope.archive = function(e) {
    e.stopPropagation()
    $scope.createArchiveProjectsModal([$scope.project]).result.then(() => {
      $scope.archiveProjects([$scope.project])
    })
  }

  $scope.unarchive = function(e) {
    e.stopPropagation()
    $scope.unarchiveProjects([$scope.project])
  }

  $scope.trash = function(e) {
    e.stopPropagation()
    $scope.createTrashProjectsModal([$scope.project]).result.then(() => {
      $scope.trashProjects([$scope.project])
    })
  }

  $scope.untrash = function(e) {
    e.stopPropagation()
    $scope.untrashProjects([$scope.project])
  }

  $scope.leave = function(e) {
    e.stopPropagation()
    $scope.createLeaveProjectsModal([$scope.project]).result.then(() => {
      $scope.leaveProjects([$scope.project])
    })
  }

  $scope.delete = function(e) {
    e.stopPropagation()
    $scope.createDeleteProjectsModal([$scope.project]).result.then(() => {
      $scope.deleteProjects([$scope.project])
    })
  }
})
